package cz.czu.pef.DataModelling.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cz.czu.pef.DataModelling.dao.Dao;
import cz.czu.pef.DataModelling.domainObjects.IdObject;
import cz.czu.pef.DataModelling.entityObjects.IdPersistenceEntityObject;
import cz.czu.pef.DataModelling.service.api.AbstractWrapperService;
import cz.czu.pef.DataModelling.utils.TransformEntityBean;
import cz.czu.pef.common.CustomFilterField;
import cz.czu.pef.common.CustomSortField;

/**
 * Implements:
 * - basic DAO operations on given Entity
 * - composed DAO operations with shared implementation logic
 * 
 * @author Rostislav Stříbrný
 *
 * @param <T>
 */
@SuppressWarnings("unchecked")
public abstract class AbstractWrapperServiceImpl<TD extends IdObject, TE extends IdPersistenceEntityObject> implements AbstractWrapperService<TD, TE>
{
	public AbstractWrapperServiceImpl()
	{
	}

	public abstract Dao<TE> getDao();
    @Autowired TransformEntityBean transformEntityBean;

    public Set<TD> transformSetTE(Set<TE> input)
    {
      Set<TD> output = new HashSet<TD>();
      Iterator<TE> iter = input.iterator();
      while (iter.hasNext())
      {
        TE item = iter.next();
        if (item == null)
        {
          output.add((TD)null);
          continue;
        }
        output.add((TD)transformEntityBean.transform(item));
      }
      return output;
    }

    public List<TD> transformListTE(List<TE> input)
    {
      List<TD> output = new ArrayList<TD>();
      for (int i=0;i<input.size(); i++)
      {
        TE item = input.get(i);
        if (item == null)
        {
          output.add((TD)null);
          continue;
        }
        output.add((TD)transformEntityBean.transform(item));
      }
      return output;
    }

    public Set<TE> transformSetTD(Set<TD> input)
    {
      Set<TE> output = new HashSet<TE>();
      Iterator<TD> iter = input.iterator();
      while (iter.hasNext())
      {
        TD item = iter.next();
        if (item == null)
        {
          output.add((TE)null);
          continue;
        }
        output.add((TE)transformEntityBean.transform(item));
      }
      return output;
    }

    public List<TE> transformListTD(List<TD> input)
    {
      List<TE> output = new ArrayList<TE>();
      for (int i=0;i<input.size(); i++)
      {
        TD item = input.get(i);
        if (item == null)
        {
          output.add((TE)null);
          continue;
        }
        output.add((TE)transformEntityBean.transform(item));
      }
      return output;
    }

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public TD getByStringId(String id)
	{
	  TE item = getDao().getByStringId(id);
	  if (item == null)
	   return null;
	  return (TD)transformEntityBean.transform(item);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public void persist(TD entity)
	{
		getDao().persist((TE)transformEntityBean.transform(entity));
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public TD getById(long id)
	{
	  TE item = (TE)getDao().getById(id);
	  if (item == null)
	    return null;
	  return (TD)transformEntityBean.transform(item);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public TD getByIdLocked(long id)
	{
	  TE item = getDao().getByIdLocked(id);
	  if (item == null)
	    return null;
	  return (TD)transformEntityBean.transform(item);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public void remove(TD entity)
	{
	  TE item = (TE)transformEntityBean.transform(entity);
	  getDao().remove(item);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public void removeById(long idValue)
	{
		getDao().removeById(idValue);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public TD merge(TD entity)
	{
	  TE item = (TE)transformEntityBean.transform(entity);
	  item = getDao().merge(item);
	  return (TD)transformEntityBean.transform(item);
	}

    @Override
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public TD mergeUnique(final String attribName, final Object value, TD entity)
    {
      TE item = (TE)transformEntityBean.transform(entity);
      item = getDao().mergeUnique(attribName, value, item);
      return (TD)transformEntityBean.transform(item);
    }

    @Override
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public TD mergeUniqueExt(final String[] attribNames, final Object[] values, TD entity)
    {
      TE item = (TE)transformEntityBean.transform(entity);
      item = getDao().mergeUniqueExt(attribNames, values, item);
      return (TD)transformEntityBean.transform(item);
    }

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public Long count()
	{
		return getDao().count();
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public Long countWithFilter(List<CustomFilterField> filterFields)
	{
		return getDao().countWithFilter(filterFields);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findByAttributeIn(String attribName, Collection<? extends Object> value)
	{
	  List<TE> itemsTE = getDao().findByAttributeIn(attribName, value);
	  List<TD> itemsTD = transformListTE(itemsTE);
	  return itemsTD;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findByAttribute(String attribName, Object value)
	{
	  List<TE> itemsTE = getDao().findByAttribute(attribName, value);
      List<TD> itemsTD = transformListTE(itemsTE);
      return itemsTD;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public TD findByAttributeUniqueResult(String attribName, Object value)
	{
	  TE item = getDao().findByAttributeUniqueResult(attribName, value);
	  if (item == null)
	    return null;
      return (TD)transformEntityBean.transform(item);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findAll()
	{
	  List<TE> itemsTE = getDao().findAll();
      List<TD> itemsTD = transformListTE(itemsTE);
      return itemsTD;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findAllByRange(Integer firstRow, Integer endRow)
	{
	  List<TE> itemsTE = getDao().findAllByRange(firstRow, endRow);
      List<TD> itemsTD = transformListTE(itemsTE);
      return itemsTD;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findAllByRangeWithFilter(Integer firstRow, Integer endRow, List<CustomFilterField> filterFields)
	{
	  List<TE> itemsTE = getDao().findAllByRangeWithFilter(firstRow, endRow, filterFields);
      List<TD> itemsTD = transformListTE(itemsTE);
      return itemsTD;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
	public List<TD> findAllByRangeWithFilterAndSort(Integer firstRow, Integer endRow, List<CustomFilterField> filterFields, List<CustomSortField> sortFields)
	{
	  List<TE> itemsTE = getDao().findAllByRangeWithFilterAndSort(firstRow, endRow, filterFields, sortFields);
      List<TD> itemsTD = transformListTE(itemsTE);
      return itemsTD;
	}

    @Override
    @Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    public boolean canBeRemoved(TD entity)
    {
        if (entity == null)
            return true;

        TE objectEntity = getDao().getById(entity.getId());
        if (objectEntity == null)
            return true;

        return objectEntity.canBeRemoved();
    }
}
